﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace NMoq.Core
{
    public class MockProxyFactory
    {
        #region create proxy object
        /// <summary>
        /// Create a proxy object of type T ,with a args constructor 
        /// </summary>
        /// <typeparam name="T">T should be inherited from MarshalByRefObject</typeparam>
        /// <param name="args">the constructor's parameters</param>
        /// <returns></returns>
        public static T Create<T>(params object[] args)
        {
            return CreateProxy<T>(args).ProxiedTarget;
        }

        /// <summary>
        /// Create a proxied object of target with 'target' instance
        /// </summary>
        /// <typeparam name="T">T should be an interface type or should be inherited from MarshalByRefObject</typeparam>
        /// <param name="target">an instance of T</param>
        /// <returns></returns>
        public static T CreateWithTarget<T>(T target)
        {
            return CreateProxy<T>(target).ProxiedTarget;
        }
        #endregion

        #region using RealProxy to create proxy
        /// <summary>
        /// Create a RealProxy of type T, and the T should have a constructor that has the args match the parameter
        /// </summary>
        /// <typeparam name="T">T should be inherited from MarshalByRefObject</typeparam>
        /// <param name="args"></param>
        /// <returns></returns>
        public static MockProxy<T> CreateProxy<T>(params object[] args)
        {
            if (typeof(T).IsClass && typeof(T).IsMarshalByRef)
            {                 
                ConstructorInfo constructor = typeof(T).GetConstructor(Type.GetTypeArray (args ));

                if (constructor == null)
                    throw new MockException("There is no contructor match the args");

                // invoke constructor
                T target = (T)constructor.Invoke(args);
                MockProxy<T> targetProxy = new MockProxy<T>(target);
                return targetProxy;
            }
            throw new InvalidOperationException("The generic type T is not inherited from MarshalByRefObject");
        }

        /// <summary>
        /// Create a RealProxy of target 
        /// </summary>
        /// <typeparam name="T">T should be inherited from MarshalByRefObject ,  or T is an interface type</typeparam>
        /// <param name="target">an instance of T</param>
        /// <returns></returns>
        public static MockProxy<T> CreateProxyWithTarget<T>(T target)
        {
            if (IsProxiedable(typeof (T)))
            { 
                MockProxy<T> targetProxy = new MockProxy<T>(target);
                return targetProxy;
            }
            throw new InvalidOperationException("The generic type T is not inherited from MarshalByRefObject");
        }
        #endregion

        #region using System.Reflection.Emit to create proxy
        #endregion

        #region assistant method
        /// <summary>
        /// Is the targetType could be proxied,
        /// if targetType is an interface or targetType is inherited from MarshalByRefObject,
        /// it can be proxied
        /// </summary>
        /// <param name="targetType"></param>
        /// <returns></returns>
        public static bool IsProxiedable(Type targetType)
        {
            return (targetType.IsClass && targetType.IsMarshalByRef) || targetType.IsInterface;
        }
        #endregion
    }
}
